library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.numeric_std.all;


entity quattro is
generic (		NUMBIT	: integer := 4;
			ROMSIZE	: integer := 8;
			RAMSIZE	: integer := 4);
port(	clock	 			: in  std_logic;
		reset	 			: in  std_logic;					
		final_result			: out std_logic_vector (NUMBIT-1 downto 0)
);
end quattro;

architecture Structural of quattro is

component ROM is
generic (	N_DATA	: natural := 4;
		N_ROWS	: natural := 8);
port(	ADDR	: in   std_logic_vector (N_DATA-1 downto 0);
		I		: out std_logic_vector (N_DATA*2-1 downto 0)
);
end component;

component one_adder is
generic (	N : integer := 32);
port(	data_in	: in   std_logic_vector (N-1 downto 0);
		data_out	: out std_logic_vector (N-1 downto 0)
);
end component;

component Instr_reg is
generic(	numBit	: integer := 32;
		opSize	: integer := 4);
port(	clock	: in   std_logic;
		reset	: in   std_logic;
		enable	: in   std_logic;
		data_in 	: in   std_logic_vector (numBit-1 downto 0);
		opcode 	: out std_logic_vector (opSize-1 downto 0);
		r_s 		: out std_logic;
		r_t 		: out std_logic;
		r_d 		: out std_logic_vector (1 downto 0);
		Imm		: out std_logic_vector (3 downto 0)
);
end component;

component Register_File is
generic ( N 		: positive := 4;		--number of bits in the registers
		N_ADDR	: positive := 2);		--address size (determines number of registers)
port(	clock 		: in   std_logic;
		reset 		: in   std_logic;
		port_S_addr	: in   std_logic;
		port_T_addr	: in   std_logic;
		port_D_addr	: in   std_logic_vector (1 downto 0);
		write_D_EN	: in   std_logic;
		port_D_IN	: in   std_logic_vector (N-1 downto 0);  --content to be written (Data In)
		port_S_OUT	: out std_logic_vector (N-1 downto 0);  --content to be read
		port_T_OUT	: out std_logic_vector (N-1 downto 0)   --content to be read
);
end component;

component arithmetic_logic_unit is
generic (N : integer := 32);
port(	left		: in   std_logic_vector (N-1 downto 0);	-- left operand (A or NPC)
		right		: in   std_logic_vector (N-1 downto 0);	-- right operand (B or Imm)
		alu_mux	: in   std_logic_vector (2 downto 0);	-- operation to perform (activates the correct output)
		output	: out std_logic_vector (N-1 downto 0)	-- ALU output
);
end component;

component RAM is
generic (		DATA : integer := 32;
			ADDR : integer := 8);
port( 	clock	: in   std_logic;
		reset	: in   std_logic;
		write_en	: in   std_logic;
		addr_in 	: in   std_logic_vector (ADDR-1 downto 0);
		data_in	: in   std_logic_vector (DATA-1 downto 0);
		data_out	: out std_logic_vector (DATA-1 downto 0)  
);
end component;

component muxer
generic (N : integer := 32);
port(	data_0 	: in   std_logic_vector (N-1 downto 0);
		data_1 	: in   std_logic_vector (N-1 downto 0);
		sel 	 	: in   std_logic;
		output 	: out std_logic_vector (N-1 downto 0)
);
end component;

component reg is
generic (N : integer :=32);
port(	clock	: in   std_logic;
		reset	: in   std_logic;
		enable	: in   std_logic;
		data_in 	: in   std_logic_vector (N-1 downto 0);
		data_out : out std_logic_vector (N-1 downto 0)
);
end component;
component Control_Unit is
generic(	opSize	: integer := 4);
port(	clk			: in   std_logic;
		rst			: in   std_logic;
		opcode		: in   std_logic_vector (opSize-1 downto 0);		-- opcode form the IR
		alu_mux		: out std_logic_vector (2 downto 0);			-- operation sent to the ALU
		right_mux		: out std_logic;							-- right muxer pilot
		mem_wr		: out std_logic;							-- read or write in the datamem
		w_en		: out std_logic;							-- write enable for the register file
		is_jump		: out std_logic;							-- control the jump muxer in MM
		wb_mux 		: out std_logic;							-- writeback muxer pilot	
		en_if 		: out std_logic;
		en_id 		: out std_logic;
		en_ex 		: out std_logic;
		en_mm 		: out std_logic
);
end component;
------------------------------------------------------
--     Signal list used to connect the devices      --
signal i_opcode 		: std_logic_vector (NUMBIT-1 downto 0);		-- connects IF to ID and CU
signal i_alu_mux		: std_logic_vector (2 downto 0);				-- connects CU to EX
signal i_is_jump		: std_logic;								-- connects CU to EX
signal i_right_mux		: std_logic;								-- connects CU to EX
signal i_mem_wr		: std_logic;								-- connects CU to MM
signal i_w_en			: std_logic;								-- connects CU to ID
signal i_wb_mux		: std_logic;								-- connects CU to WB
signal i_r_s			: std_logic;								-- connects IF to ID
signal i_r_t			: std_logic;								-- connects IF to ID and CU
signal i_r_d			: std_logic_vector (1 downto 0);				-- connects IF to CU
signal i_memA_out		: std_logic_vector (3 downto 0);				-- connects CU to ID
signal i_memB_out		: std_logic_vector (3 downto 0);				-- connects CU to ID
signal i_Imm			: std_logic_vector (NUMBIT-1 downto 0);		-- connects CU to ID
signal i_NPC_out		: std_logic_vector (NUMBIT-1 downto 0);		-- connects IF to EX and MM
signal i_A_out			: std_logic_vector (NUMBIT-1 downto 0);		-- connects ID to EX
signal i_B_out			: std_logic_vector (NUMBIT-1 downto 0);		-- connects ID to EX and MM
signal i_Imm_out		: std_logic_vector (NUMBIT-1 downto 0);		-- connects ID to EX
signal i_ALU_output		: std_logic_vector (NUMBIT-1 downto 0);		-- connects EX to MM
signal i_result			: std_logic_vector (NUMBIT-1 downto 0);		-- connects MM to WB
signal i_NPC_final		: std_logic_vector (NUMBIT-1 downto 0);		-- connects MM to IF
signal i_NPC_tmp		: std_logic_vector (NUMBIT-1 downto 0);		-- connects MM to IF
signal i_output			: std_logic_vector (NUMBIT-1 downto 0);		-- connects WB to ID
signal i_ALU_in_r 		: std_logic_vector (numBit-1 downto 0); 
signal i_ALU_tmp  		: std_logic_vector (numBit-1 downto 0);
signal i_pc_out		: std_logic_vector (numBit - 1 downto 0);
signal i_adder_out	: std_logic_vector (numBit - 1 downto 0);
signal i_IRmem_out	: std_logic_vector (numBit*2 - 1 downto 0);
--		 Signal list used for enabling registers  	 --
signal i_en_if 		: std_logic;
signal i_en_id 		: std_logic;
signal i_en_ex		: std_logic;
signal i_en_mm 	: std_logic;
------------------------------------------------------
begin

CU: Control_Unit generic 
map ( opSize 	=> NUMBIT ) port
map (	clk			=> clock,
		rst			=> reset,
		opcode		=> i_opcode,
		alu_mux		=> i_alu_mux,
		right_mux		=> i_right_mux,
		mem_wr		=> i_mem_wr,
		w_en		=> i_w_en,
		is_jump		=> i_is_jump,
		wb_mux 		=> i_wb_mux,
		en_if 		=> i_en_if,
		en_id 		=> i_en_id,
		en_ex 		=> i_en_ex,
		en_mm 		=> i_en_mm
);

PC: reg generic map(numBit) port 
map (clock, reset, '1', i_NPC_final, i_pc_out);

Adder1: one_adder generic map(numBit) port
map (i_pc_out, i_adder_out);

NPC: reg generic map(numBit) port
map (clock, reset, i_en_if, i_adder_out, i_NPC_out);

MEMROM: ROM generic
map (	N_DATA	=> numBit,
		N_ROWS	=> romSize) port
map (	ADDR	=> i_pc_out,
		I		=> i_IRmem_out
);

IR: Instr_reg generic
map (	numBit	=> numBit*2,
		opSize	=> numBit ) port
map (	clock	=> clock,
		reset	=> reset,
		enable	=> i_en_if,
		data_in 	=> i_IRmem_out,
		opcode 	=> i_opcode,
		r_s 		=> i_r_s,
		r_t 		=> i_r_t,
		r_d 		=> i_r_d,
		Imm		=> i_Imm
);

REG_FILE: Register_File generic map (numBit,2) port
map (	clock 		=> clock,
		reset 		=> reset,
		port_S_addr	=> i_r_s,
		port_T_addr	=> i_r_t,
		port_D_addr	=> i_r_d,
		write_D_EN	=> i_w_en,
		port_D_IN	=> i_output,
		port_S_OUT	=> i_memA_out,
		port_T_OUT	=> i_memB_out
);


A: reg generic map (numBit) port 
map (clock, reset, i_en_id, i_memA_out, i_A_out);

B: reg generic map (numBit) port 
map (clock, reset, i_en_id, i_memB_out, i_B_out);

Imm: reg generic map (numBit) port 
map (clock, reset, i_en_id, i_Imm, i_Imm_out);

MUX_R : muxer generic map(numBit) port 
map (	data_0	=> i_B_out,
		data_1	=> i_Imm_out,
		sel 		=> i_right_mux,
		output 	=> i_ALU_in_r
);

ALU: arithmetic_logic_unit generic map(numBit) port
map (	left		=> i_A_out,
		right		=> i_ALU_in_r,
		alu_mux	=> i_alu_mux,
		output	=> i_ALU_tmp
);

ALU_reg: reg generic map(numBit) port
map (	clock		=> clock,
		reset		=> reset,
		enable		=> i_en_ex,
		data_in		=> i_ALU_tmp,
		data_out 		=> i_ALU_output
);

MUX_NPC	: muxer generic map (numBit) port
map ( i_NPC_out,i_ALU_output,i_is_jump,i_NPC_tmp );

PCreg : reg generic map(numBit) port
map ( clock,reset,i_en_mm,i_NPC_tmp,i_NPC_final );

MEM	: RAM generic map (numBit,numBit) port
map (	clock		=> clock,
		reset		=> reset,
		write_en		=> i_mem_wr,
		addr_in		=> i_ALU_output,	-- RAM address
		data_in		=> i_B_out,		-- content to be written
		data_out		=> i_result
);

WB: muxer generic map(N => NUMBIT) port
map (	data_0	=> i_result,
		data_1	=> i_ALU_output,
		sel 	   	=> i_wb_mux,
		output	=> i_output
);

final_result 	<= i_output;
end Structural;
